// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0


TestCase := Rectangle {
    width: 300phx;
    height: 300phx;
    property<[{name: string, account: string, score: float}]> model: [
        {
            name: "Olivier",
            account: "ogoffart",
            score: 456,
        },
        {
            name: "Simon",
            account: "tronical",
            score: 789,
        }
    ];

    property <int> clicked_score;
    property <string> clicked_name;
    property <int> clicked_internal_state;
    property <int> clicked_index;

    for person[i] in model: TouchArea {
        x: i*10phx;
        width: 10phx;
        height: 10phx;
        property <length> model; // this is not the model
        property <string> text: person.name;
        property <int> score: person.score * 1000;
        property <int> internal_state: 0;
        clicked => {
            internal_state += 1;
            clicked_internal_state = internal_state;
            clicked_score = score;
            clicked_name = text;
            clicked_index = i;
        }
    }
}

/*
```rust
let instance = TestCase::new().unwrap();

// there should be nothing there
slint_testing::send_mouse_click(&instance, 25., 5.);
assert_eq!(instance.get_clicked_score(), 0);
assert_eq!(instance.get_clicked_internal_state(), 0);

slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq!(instance.get_clicked_score(), 789000);
assert_eq!(instance.get_clicked_internal_state(), 1);

type ModelData = (slint::SharedString, slint::SharedString, f32);
let another_model = std::rc::Rc::new(slint::VecModel::<ModelData>::from(
    vec![
        ("a1".into(), "hello".into(), 111.),
        ("a2".into(), "cruel".into(), 222.),
        ("a3".into(), "world".into(), 333.),
    ]));

instance.set_model(slint::ModelRc::from(another_model.clone()));

slint_testing::send_mouse_click(&instance, 25., 5.);
assert_eq!(instance.get_clicked_score(), 333000);
assert_eq!(instance.get_clicked_internal_state(), 1);
assert_eq!(instance.get_clicked_index(), 2);

slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq!(instance.get_clicked_score(), 222000);
assert_eq!(instance.get_clicked_name(), slint::SharedString::from("cruel"));
assert_eq!(instance.get_clicked_internal_state(), 1);

another_model.push(("a4".into(), "!".into(), 444.));
slint_testing::send_mouse_click(&instance, 35., 5.);
assert_eq!(instance.get_clicked_score(), 444000);
assert_eq!(instance.get_clicked_name(), slint::SharedString::from("!"));
assert_eq!(instance.get_clicked_internal_state(), 1);

use slint::Model;
another_model.set_row_data(1, ("a2".into(), "idyllic".into(), 555.));
slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq!(instance.get_clicked_score(), 555000);
assert_eq!(instance.get_clicked_name(), slint::SharedString::from("idyllic"));
assert_eq!(instance.get_clicked_internal_state(), 2);
assert_eq!(instance.get_clicked_index(), 1);

another_model.remove(1);
slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq!(instance.get_clicked_score(), 333000);
assert_eq!(instance.get_clicked_name(), slint::SharedString::from("world"));
assert_eq!(instance.get_clicked_internal_state(), 2);
assert_eq!(instance.get_clicked_index(), 1);

```


```rust
// Test a broken model
// The behavior is not really defined, but at least it shouldn't panic

let instance = TestCase::new().unwrap();

type ModelData = (slint::SharedString, slint::SharedString, f32);
struct BrokenModel;

impl slint::Model for BrokenModel {
    type Data = ModelData;
    fn row_count(&self) -> usize { 3 }

    fn row_data(&self, row: usize) -> Option<Self::Data> {
        // The model is "broken" because it doesn't return anything for when row == 1
        if row == 0 || row == 2 {
            Some(("Hello".into(), "World".into(), row as f32 + 42.))
        } else {
            None
        }
    }
    fn model_tracker(&self) -> &dyn slint::ModelTracker {
        &()
    }
}

slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq!(instance.get_clicked_score(), 789000);
assert_eq!(instance.get_clicked_internal_state(), 1);

instance.set_model(slint::ModelRc::new(BrokenModel));

slint_testing::send_mouse_click(&instance, 25., 5.);
assert_eq!(instance.get_clicked_index(), 2);
assert_eq!(instance.get_clicked_score(), 44000);

// At position 0 there is the invalid item
slint_testing::send_mouse_click(&instance, 5., 5.);
assert_eq!(instance.get_clicked_score(), 0);
assert_eq!(instance.get_clicked_index(), 0);

```



```cpp
auto handle = TestCase::create();
const TestCase &instance = *handle;

// there should be nothing there
slint_testing::send_mouse_click(&instance, 25., 5.);
assert_eq(instance.get_clicked_score(), 0);
assert_eq(instance.get_clicked_internal_state(), 0);

slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq(instance.get_clicked_score(), 789000);
assert_eq(instance.get_clicked_internal_state(), 1);

using ModelData = std::tuple<slint::SharedString, slint::SharedString, float>;
std::vector<ModelData> array;
array.push_back(ModelData{"a1", "hello", 111.});
array.push_back(ModelData{"a2", "cruel", 222.});
array.push_back(ModelData{"a3", "world", 333.});
auto another_model = std::make_shared<slint::VectorModel<ModelData>>(std::move(array));
instance.set_model(another_model);

slint_testing::send_mouse_click(&instance, 25., 5.);
assert_eq(instance.get_clicked_score(), 333000);
assert_eq(instance.get_clicked_internal_state(), 1);
assert_eq(instance.get_clicked_index(), 2);

slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq(instance.get_clicked_score(), 222000);
assert_eq(instance.get_clicked_name(), "cruel");
assert_eq(instance.get_clicked_internal_state(), 1);

another_model->push_back({"a4", "!", 444.});
slint_testing::send_mouse_click(&instance, 35., 5.);
assert_eq(instance.get_clicked_score(), 444000);
assert_eq(instance.get_clicked_name(), "!");
assert_eq(instance.get_clicked_internal_state(), 1);

another_model->set_row_data(1, {"a2", "idyllic", 555.});
slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq(instance.get_clicked_score(), 555000);
assert_eq(instance.get_clicked_name(), "idyllic");
assert_eq(instance.get_clicked_internal_state(), 2);
assert_eq(instance.get_clicked_index(), 1);

another_model->erase(1);
slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq(instance.get_clicked_score(), 333000);
assert_eq(instance.get_clicked_name(), "world");
assert_eq(instance.get_clicked_internal_state(), 2);
assert_eq(instance.get_clicked_index(), 1);
```

```cpp
// Test a broken model
// The behavior is not really defined, but at least it shouldn't panic

auto handle = TestCase::create();
const TestCase &instance = *handle;

using ModelData = std::tuple<slint::SharedString, slint::SharedString, float>;
struct BrokenModel : slint::Model<ModelData> {
    std::size_t row_count() const override { return 3; }
    std::optional<ModelData> row_data(std::size_t row) const override {
        // The model is "broken" because it doesn't return anything for when row == 1
        if (row == 0 || row == 2) {
            return ModelData{"Hello", "World", row + 42.};
        } else {
            return std::nullopt;
        }
    }
};

slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq(instance.get_clicked_score(), 789000);
assert_eq(instance.get_clicked_internal_state(), 1);

auto model = std::make_shared<BrokenModel>();
instance.set_model(model);

slint_testing::send_mouse_click(&instance, 25., 5.);
assert_eq(instance.get_clicked_index(), 2);
assert_eq(instance.get_clicked_score(), 44000);

// At position 0 there is the invalid item
slint_testing::send_mouse_click(&instance, 5., 5.);
assert_eq(instance.get_clicked_score(), 0);
assert_eq(instance.get_clicked_index(), 0);
```

```js
var instance = new slint.TestCase({});

// there should be nothing there
slintlib.private_api.send_mouse_click(instance, 25., 5.);
assert.equal(instance.clicked_score, 0);
assert.equal(instance.clicked_internal_state, 0);

slintlib.private_api.send_mouse_click(instance, 15., 5.);
assert.equal(instance.clicked_score, 789000);
assert.equal(instance.clicked_internal_state, 1);

let another_model = new slintlib.ArrayModel([
    {account: "a1", name: "hello", score: 111.},
    {account: "a2", name: "cruel", score: 222.},
    {account: "a3", name: "world", score: 333.},
]);
instance.model = another_model;

let roundtripped_model = instance.model;
assert.equal(another_model, roundtripped_model);

slintlib.private_api.send_mouse_click(instance, 25., 5.);
assert.equal(instance.clicked_score, 333000);
assert.equal(instance.clicked_internal_state, 1);
assert.equal(instance.clicked_index, 2);

slintlib.private_api.send_mouse_click(instance, 15., 5.);
assert.equal(instance.clicked_score, 222000);
assert.equal(instance.clicked_name, "cruel");
assert.equal(instance.clicked_internal_state, 1);

another_model.push({account: "a4", name: "!", score: 444.});
slintlib.private_api.send_mouse_click(instance, 35., 5.);
assert.equal(instance.clicked_score, 444000);
assert.equal(instance.clicked_name, "!");
assert.equal(instance.clicked_internal_state, 1);

another_model.setRowData(1, {account: "a2", name: "idyllic", score: 555.});
slintlib.private_api.send_mouse_click(instance, 15., 5.);
assert.equal(instance.clicked_score, 555000);
assert.equal(instance.clicked_name, "idyllic");
assert.equal(instance.clicked_internal_state, 2);
assert.equal(instance.clicked_index, 1);

another_model.remove(1, 1);
slintlib.private_api.send_mouse_click(instance, 15., 5.);
assert.equal(instance.clicked_score, 333000);
assert.equal(instance.clicked_name, "world");
assert.equal(instance.clicked_internal_state, 2);
assert.equal(instance.clicked_index, 1);
```

////////// Issue #8021
```cpp
using ModelData = std::tuple<slint::SharedString, slint::SharedString, float>;
static auto mk_data = [](slint::SharedString name) { return ModelData{name, name, 0.}; };
class MyModel : public slint::Model<ModelData> {
    std::vector<ModelData> data;

  public:
    MyModel() {
        data.push_back(mk_data("John"));
        data.push_back(mk_data("Mary"));
        data.push_back(mk_data("Bob"));
    }
    std::size_t row_count() const override {return data.size();}
    std::optional<ModelData> row_data(std::size_t i) const override {
        return (i < data.size()) ? std::make_optional(data.at(i)) : std::nullopt;
    }
    void update() {
        data.push_back(mk_data("Olivier"));
        notify_reset();
        notify_row_changed(data.size() - 1);
    }
};

auto handle = TestCase::create();
const TestCase &instance = *handle;


auto model = std::make_shared<MyModel>();
instance.set_model(model);
slint_testing::send_mouse_click(&instance, 15., 5.);
assert_eq(instance.get_clicked_name(), "Mary");
model->update();
slint_testing::send_mouse_click(&instance, 35., 5.);
assert_eq(instance.get_clicked_name(), "Olivier");
```

*/
